// 导入用户模型（操作数据库）和Sequelize操作符（用于复杂查询）
const { User } = require("../models");
const { Op } = require("sequelize");
// 导入HTTP错误处理模块（用于返回标准HTTP错误）
const createError = require("http-errors");
// 导入bcrypt模块（用于密码加密和验证）
const bcrypt = require("bcrypt");

/**
 * 用户服务类
 * 职责：封装用户相关的所有业务逻辑，如数据校验、数据库交互、密码处理等
 * 解耦：与控制器分离，控制器仅负责接收请求和返回响应，业务逻辑集中在此
 */
class UserService {
  /**
   * 获取所有用户（支持分页、筛选）
   * @param {number} page - 页码，默认1
   * @param {number} limit - 每页条数，默认10
   * @param {Object} filters - 筛选条件（status状态、search关键词）
   * @returns {Object} 包含用户列表和分页信息的对象
   */
  async findAllUsers(page = 1, limit = 10, filters = {}) {
    try {
      // 计算分页偏移量（当前页-1）*每页条数，用于数据库limit查询
      const offset = (page - 1) * limit;
      // 构建查询条件（动态拼接，仅当筛选参数存在时添加）
      const whereClause = {};

      // 状态筛选：若传递了status，添加到查询条件
      if (filters.status) {
        whereClause.status = filters.status;
      }

      // 关键词搜索：若传递了search，模糊匹配用户名、邮箱、姓名
      if (filters.search) {
        whereClause[Op.or] = [
          { username: { [Op.like]: `%${filters.search}%` } }, // 用户名模糊匹配
          { email: { [Op.like]: `%${filters.search}%` } }, // 邮箱模糊匹配
          { firstName: { [Op.like]: `%${filters.search}%` } }, // 名字模糊匹配
          { lastName: { [Op.like]: `%${filters.search}%` } }, // 姓氏模糊匹配
        ];
      }

      // 执行分页查询：findAndCountAll返回总数（count）和当前页数据（rows）
      const { count, rows } = await User.findAndCountAll({
        where: whereClause, // 查询条件
        attributes: { exclude: ["password"] }, // 排除密码字段（安全考虑）
        order: [["createdAt", "DESC"]], // 按创建时间倒序排列（最新在前）
        limit: parseInt(limit), // 每页最大条数（转为数字类型）
        offset: offset, // 分页偏移量
      });

      // 返回格式化结果：包含用户列表和分页元信息
      return {
        users: rows, // 当前页用户数据
        pagination: {
          page: parseInt(page), // 当前页码
          limit: parseInt(limit), // 每页条数
          total: count, // 总用户数
          pages: Math.ceil(count / limit), // 总页数（向上取整，避免小数页）
        },
      };
    } catch (error) {
      // 捕获异常并包装为业务错误信息
      throw new Error(`获取用户列表失败: ${error.message}`);
    }
  }

  /**
   * 根据ID获取单个用户详情
   * @param {number/string} id - 用户ID
   * @returns {Object} 用户详情（不含密码）
   */
  async findUserById(id) {
    try {
      // 根据主键查询用户，排除密码字段
      const user = await User.findByPk(id, {
        attributes: { exclude: ["password"] },
      });

      // 若用户不存在，返回404错误（HTTP标准错误）
      if (!user) {
        throw createError(404, "用户不存在");
      }

      return user;
    } catch (error) {
      // 若已是HTTP错误（如404），直接抛出；否则包装为业务错误
      if (error.status === 404) {
        throw error;
      }
      throw new Error(`获取用户失败: ${error.message}`);
    }
  }

  /**
   * 创建新用户（含数据校验、密码加密）
   * @param {Object} userData - 用户注册数据（用户名、邮箱、密码等）
   * @returns {Object} 新创建的用户信息（不含密码）
   */
  async createUser(userData) {
    try {
      // 解构用户数据（提取需要的字段，避免冗余参数）
      const { username, email, password, firstName, lastName } = userData;

      // 1. 校验用户名/邮箱是否已存在（避免重复注册）
      const existingUser = await User.findOne({
        where: {
          [Op.or]: [{ username }, { email }], // 用户名或邮箱任一重复即拦截
        },
      });

      // 若已存在，返回409错误（HTTP状态码：资源冲突）
      if (existingUser) {
        throw createError(409, "用户名或邮箱已存在");
      }

      // 2. 密码加密：使用bcrypt，盐值 rounds 优先从环境变量获取，默认12
      const hashedPassword = await bcrypt.hash(
        password,
        parseInt(process.env.BCRYPT_SALT_ROUNDS) || 12 // 盐值强度（越高越安全但耗时）
      );

      // 3. 写入数据库：存储加密后的密码，而非明文
      const user = await User.create({
        username,
        email,
        password: hashedPassword, // 存储加密密码
        firstName,
        lastName,
      });

      // 4. 处理返回结果：转为JSON并删除密码字段（安全考虑）
      const userWithoutPassword = user.toJSON();
      delete userWithoutPassword.password;

      return userWithoutPassword;
    } catch (error) {
      // 若已是HTTP错误（如409），直接抛出；否则包装为业务错误
      if (error.status === 409) {
        throw error;
      }
      throw new Error(`创建用户失败: ${error.message}`);
    }
  }

  /**
   * 更新用户信息（仅允许更新指定字段，避免越权）
   * @param {number/string} id - 待更新用户ID
   * @param {Object} userData - 待更新的用户数据（姓名、状态）
   * @returns {Object} 更新后的用户信息（不含密码）
   */
  async updateUser(id, userData) {
    try {
      // 仅提取允许更新的字段（避免更新用户名、邮箱、密码等敏感字段）
      const { firstName, lastName, status } = userData;

      // 1. 先查询用户是否存在
      const user = await User.findByPk(id);
      if (!user) {
        throw createError(404, "用户不存在");
      }

      // 2. 动态更新字段：仅当参数存在时才更新（避免覆盖原有值为undefined）
      if (firstName !== undefined) user.firstName = firstName;
      if (lastName !== undefined) user.lastName = lastName;
      if (status !== undefined) user.status = status;

      // 3. 保存更新到数据库
      await user.save();

      // 4. 处理返回结果：删除密码字段
      const userWithoutPassword = user.toJSON();
      delete userWithoutPassword.password;

      return userWithoutPassword;
    } catch (error) {
      // 若已是HTTP错误（如404），直接抛出；否则包装为业务错误
      if (error.status === 404) {
        throw error;
      }
      throw new Error(`更新用户失败: ${error.message}`);
    }
  }

  /**
   * 删除用户（物理删除，若需逻辑删除可改为更新status字段）
   * @param {number/string} id - 待删除用户ID
   * @returns {Object} 删除成功的提示信息
   */
  async deleteUser(id) {
    try {
      // 1. 查询用户是否存在
      const user = await User.findByPk(id);
      if (!user) {
        throw createError(404, "用户不存在");
      }

      // 2. 执行删除操作（物理删除：从数据库中移除记录）
      await user.destroy();

      // 3. 返回成功提示
      return { message: "用户删除成功" };
    } catch (error) {
      // 若已是HTTP错误（如404），直接抛出；否则包装为业务错误
      if (error.status === 404) {
        throw error;
      }
      throw new Error(`删除用户失败: ${error.message}`);
    }
  }

  /**
   * 根据用户名查找用户（用于登录认证）
   * @param {string} username - 用户名
   * @returns {Object|null} 用户信息（含密码，用于后续密码验证）
   */
  async findUserByUsername(username) {
    try {
      // 按用户名精确查询，返回完整用户信息（含加密密码，供认证使用）
      const user = await User.findOne({
        where: { username },
      });

      return user; // 若不存在则返回null
    } catch (error) {
      throw new Error(`查找用户失败: ${error.message}`);
    }
  }

  /**
   * 验证密码（对比明文密码与加密密码是否匹配）
   * @param {string} plainPassword - 用户输入的明文密码
   * @param {string} hashedPassword - 数据库中存储的加密密码
   * @returns {boolean} 匹配结果（true=匹配，false=不匹配）
   */
  async validatePassword(plainPassword, hashedPassword) {
    try {
      // 使用bcrypt.compare对比密码：自动处理盐值，无需手动提取
      return await bcrypt.compare(plainPassword, hashedPassword);
    } catch (error) {
      throw new Error(`密码验证失败: ${error.message}`);
    }
  }
}

// 导出用户服务实例（单例模式，避免重复创建）
module.exports = new UserService();
